Gestion de projet, Agilité, Tests

Pour suivre en live…​

http://bit.ly/jmb-agile

http://jmbhome.github.io/teaching/topics/agile.html

 

À l’initiateur de ce cours, Jean-Michel Inglebert…​

blasonJMI

Gestion de projets

C’est quoi 1 projet ?

On appelle projet un ensemble finalisé d’activités et d’actions entreprises dans le but de répondre à un besoin défini dans des délais fixés et dans la limite d’une enveloppe budgétaire allouée.

Wikipedia
2014

 

Répondre au client
(qui paye)
dans la vraie vie
(les ressources sont limitées ET les obstacles matériels et humains sont sans limite)
JMI
2014

OK, allons-y …​

Eviter de répondre : hier (Sinon, c’est un processus de sauvetage et plus 1 projet)

…​ entre DEBUT et FIN, comment FAIRE ?

Option 1
se débrouiller (comme dans 'FAIRE les soldes')
Option 2
dérouler un process (comme dans '(RE)FAIRE son passeport')
Option 3
réaliser un projet

Depuis le temps que la notion de projet existe …​

7 projets sur 10 arrivent en retard ou jamais !

Revue Programmez!
Janvier 2014

 

Les causes :

 

Dans tous les cas, c’est l’équipe de développement qui est fautive.

Pourquoi est-ce si difficile ?

Pourtant :

Le problème

On n’a pas de méthode infaillible qui garantisse qu’une équipe projet atteindra les objectifs.

Pourquoi ?

Il s’agit de gérer une complexité métier, technique et humaine avec des ressources limitées (temps, moyens …​)

 

Seule certitude prévisible : il faudra s’adapter aux imprévus.

Le découpage en phases d’un projet logiciel

Ça n’empêche pas de définir de grandes étapes génériques que le projet devra franchir : ses PHASES

Un projet de développement logiciel se terminera de 'manière satisfaisante' si l’équipe fournit une application fiable, robuste et maintenable qui répond aux exigences du client.

 

fiable
fait ce qu’on attend d’elle dans les conditions fixées
robuste
supporte la charge, les erreurs des utilisateurs, les pannes etc
maintenable
ne demande pas d’être réécrite pour la moindre évolution

Produire des applications

 

BUILD
make, ant, Maven, Ivy, Gradle, etc.
cf. cours suivants…​
Gestion de projets
GitLab, le site pour héberger vos codes en utilisant .

Méthodes

R.A.C.H.E

La méthode préférée des étudiants La RACHE!

Voir l’excellent site parodique http://www.la-rache.com/.

Méthodes en Cascade ou Cycle en V

waterfall

 

Le cycle en V adopte :

 

v1

 

v2

 

v123

Un projet de développement de type cycle en V se terminera de manière satisfaisante si l’équipe arrive à remonter le 'V' dans de 'bonnes conditions'.

 

Arrêt du projet
  • l’application n’est pas livrable
  • au mieux des modules/classes de base peuvent être réutilisés
Dérive temporelle
  • la phase de codage et d’intégration est raccourcie
  • le produit livré a été 'trop peu' testé

 

En résumé :

 

Limites connues
  • ne décrit pas les phases de maintenance et d’évolution
  • ne prévoit pas qu’on revienne sur les étapes de conception ?
  • courbe a posteriori : quelle est la complexité estimée du projet à son début ?
  • l’application livrée est-elle conforme à sa documentation (et vice versa) ?

Du cycle en V aux cycles en W

Partons d’un exemple trivial

Example 1. Une application web de gestion d’urls

Développer une application web capable de collecter et d’afficher les urls collectées.

Cycle 0

Mise en place d’une infrastructure d’intégration

Solution retenue :

Cycle 1

Afficher les urls collectées

Solution retenue :

Cycle 2

Saisie et collecte des urls

Solution retenue :

Bilan de l’exemple trivial

 

w12

 

w32

 

w42

 

w52

 

w5b2

Les bénéfices reconnus

  • livre une application qui marche dès le cycle 1
  • gère la complexité et les problèmes d’intégration à chaque cycle
  • s’applique à un développement nouveau comme à une évolution
  • permet au client d’affiner ses exigences

 

Rappel :

Un projet de développement logiciel se terminera de 'manière satisfaisante' si l’équipe fournit une application …​…​, …​…​. et …​…​…​.. qui répond aux exigences du client.

 

Rappel :

Un projet de développement logiciel se terminera de 'manière satisfaisante' si l’équipe fournit une application fiable, robuste et maintenable qui répond aux exigences du client.

Les cas où ça ne marche pas

  • le client est (trop) absent
  • la conduite de la méthode choisie est approximative (dérive des livraisons)
  • les tests sont à l’abandon

Les problèmes nouveaux

  • qu’elle est la capacité de l’infrastructure initiale (cycle 0)
  • automatiser les tests de non régression : XP, T/BDD …​
  • approche ascendante ⇒ refactoring …​
  • comment gérer les livraisons fréquentes : intégration continue …​
  • que devient la documentation ???

V versus W

V
  • Expression des besoins
  • Spécifications
  • Conception générale et détaillée
  • Codage et tests
  • Intégration et tests
  • Livraison (recette)

V versus W (suite)

W
  • Cycle 0
  • Cycle 1 .. N
    • choisir des fonctionnalités
    • …​ coder/tester …​
    • livrer
    • ajuster la liste des demandes

Ce qui est toujours fait

Evaluer l’atteinte des objectifs

L’atteinte de tous les objectifs (et sous-objectifs) d’un projet doit être évaluée.

Quelle que soit la 'méthode de développement' employée, tout développement logiciel doit réaliser des 'tests unitaires' et des 'tests d’intégration'.

Aujourd’hui, ON DOIT
  • écrire systématiquement des programmes de test
  • exécuter très régulièrement ces programmes de tests

La méthode Agile SCRUM

 

 

Les Acteurs SCRUM

Les acteurs dans Scrum sont :

le Product Owner
le client ou son représentant qui fait partie de l’équipe
le SCRUM Master
un animateur, facilitateur plutôt qu’un chef (de projet)
l'équipe
capable de mettre en oeuvre toutes les compétences (architecture, conception, développement, IHM, tests, documentation, etc.)

Rôles du Product Owner

Rôles du Product Owner (suite)

Rôles du SCRUM Master

Rôles du SCRUM Master (suite)

L’équipe SCRUM

Déroulement d’un projet SCRUM

scrum1

 

 

 

 

 

Les artefacts SCRUM

Le backlog de produit
la liste des exigences métier ordonnée par la valeur métier (l’importance métier pour le Product Owner)
Les backlogs de sprint
les listes des tâches à réaliser pour répondre aux exigences métier choisies pour le sprint
Les burndowns du projet
les graphiques montrant l'avancement du projet

Le backlog de produit

Les backlogs de sprint

Les backlogs de sprint (suite)

Les burndowns du projet

Burndown de sprint

sprint burndown
le burndown de sprint n’est pas nécessairement décroissant si des tâches sont ajoutées ou bien réévaluées à la hausse pour leur durée.

Burndown de produit (ou de release)

productBurndown
le backlog de release pouvant évoluer, la courbe n’est pas nécessairement décroissante.
permet d’en déduire une estimation de la date de fin de release ou une estimation de la quantité du backlog de produit qui ne sera pas réalisée à une date donnée.

Indicateurs

Vélocité
quantité du backlog de produit réalisée par l’équipe pendant un sprint (calculée en points)
Capacité
quantité du backlog de produit qui sera réalisée lors du prochain sprint

Les Tests

 

Quelle que soit la méthode de développement choisie, les Tests sont le seul moyen de garantir que le produit livré est conforme aux exigences du client.

Tests unitaires

Ce sont les plus simples. Et pourtant il s’agit …​

Exemple: implémenter le type abstrait MatriceEntier

Programme de Test des Opérations
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;

public class MatriceEntierOperationsTest extends TestCase {
  static int totalAssertions = 0;
  static int bilanAssertions = 0;

  /*
   Types des operations du type MatriceEntier
  */
  public void test_type_new_MatriceEntier() throws Exception {
    MatriceEntier m = new MatriceEntier(3,3) ;

    totalAssertions++ ;
    assertEquals("new MatriceEntier(3,3) retourne une MatriceEntier", "MatriceEntier", m.getClass().getName());
    bilanAssertions++ ;
  }

  public void test_type_get() throws Exception {
    MatriceEntier m = new MatriceEntier(3,4) ;

    totalAssertions++ ;
    assertTrue("getNbLignes() > 0", m.getNbLignes() > 0);
    bilanAssertions++ ;

    totalAssertions++ ;
    assertTrue("getNbColonnes() > 0", m.getNbColonnes() > 0);
    bilanAssertions++ ;

    for (int i=0; i<m.getNbLignes(); i++) {
     for (int j=0; j<m.getNbColonnes(); j++) {
      totalAssertions++ ;
      assertTrue("getElement() retourne un entier", (m.getElement(i,j) >= 0) || (m.getElement(i,j) < 0));
      bilanAssertions++ ;
     }
    }
  }

  public void test_type_som() throws Exception {
    MatriceEntier m = new MatriceEntier(3,4) ;

    for (int i=0; i<m.getNbLignes(); i++) {
      totalAssertions++ ;
      assertTrue("somLigne("+i+") >= 0", m.somLigne(i) >= 0);
      bilanAssertions++ ;
    }
    for (int j=0; j<m.getNbColonnes(); j++) {
      totalAssertions++ ;
      assertTrue("somColonne("+j+") >= 0", m.somColonne(j) >= 0);
      bilanAssertions++ ;
    }
  }

  public void test_type_est() throws Exception {
    MatriceEntier m = new MatriceEntier(3,3) ;
    totalAssertions++ ;
    assertTrue("estCarree() retourne un booleen", (m.estCarree() == true) || (m.estCarree() == false));
    bilanAssertions++ ;

    totalAssertions++ ;
    assertTrue("estDiagonale() retourne un booleen", (m.estDiagonale() == true) || (m.estDiagonale() == false));
    bilanAssertions++ ;

    m.setElement(0,0,1) ;
    totalAssertions++ ;
    assertTrue("estDiagonale() retourne un booleen", (m.estDiagonale() == true) || (m.estDiagonale() == false));
    bilanAssertions++ ;

    m = new MatriceEntier(3,4) ;
    totalAssertions++ ;
    assertTrue("estCarree() retourne un booleen", (m.estCarree() == true) || (m.estCarree() == false));
    bilanAssertions++ ;
  }

  public void test_type_set_mul() throws Exception {
    MatriceEntier m = new MatriceEntier(3,3) ;

    for (int i=0; i<m.getNbLignes(); i++) {
     for (int j=0; j<m.getNbColonnes(); j++) {
      totalAssertions++ ;
      assertEquals("setElement() retourne une MatriceEntier", "MatriceEntier", m.setElement(i,j,i+j).getClass().getName());
      bilanAssertions++ ;
     }
    }

    totalAssertions++ ;
    assertEquals("setPremiereDiagonale(99) retourne une MatriceEntier", "MatriceEntier", m.setPremiereDiagonale(99).getClass().getName());
    bilanAssertions++ ;

    totalAssertions++ ;
    assertEquals("setSecondeDiagonale(99) retourne une MatriceEntier", "MatriceEntier", m.setSecondeDiagonale(99).getClass().getName());
    bilanAssertions++ ;

    totalAssertions++ ;
    assertEquals("mulMatNombre() retourne une MatriceEntier", "MatriceEntier", m.mulMatNombre(33).getClass().getName());
    bilanAssertions++ ;
  }

  /*
   main() de la classe de Test
  */
  public static void main(String[] args) {
    junit.textui.TestRunner.run(new TestSuite(MatriceEntierOperationsTest.class));
    if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
    else  { System.out.print("OUPS !"); }
    System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
  } // fin main

} // fin MatriceEntierOperationsTest
Programme de Test des Préconditions
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;

public class MatriceEntierPreconditionsTest extends TestCase {
  static int totalAssertions = 0;
  static int bilanAssertions = 0;

  /*
   Préconditions du type Pile
  */
  public void test_precondition1() {
    MatriceEntier m ;
    boolean exception = false ;
    try { m = new MatriceEntier(0,1) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("new MatriceEntier(0,1) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m = new MatriceEntier(1,0) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("new MatriceEntier(1,0) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m = new MatriceEntier(0,0) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("new MatriceEntier(0,0) leve une exception", exception);
    bilanAssertions++ ;
  }

  public void test_precondition2() throws Exception {
    MatriceEntier m = new MatriceEntier(2,3);
    boolean exception = false ;
    try { m.getElement(-1,1) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("getElement(-1,1) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.getElement(2,2) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("getElement(2,2) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.getElement(1,-1) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("getElement(1,-1) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.getElement(1,3) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("getElement(1,3) leve une exception", exception);
    bilanAssertions++ ;
  }

  public void test_precondition3() throws Exception {
    MatriceEntier m = new MatriceEntier(2,3);
    boolean exception = false ;
    try { m.setElement(-1,1,99) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("setElement(-1,1,99) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.setElement(2,2,99) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("setElement(2,2,99) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.setElement(1,-1,99) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("setElement(1,-1,99) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.setElement(1,3,99) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("setElement(1,3,99) leve une exception", exception);
    bilanAssertions++ ;
  }

  public void test_precondition4() throws Exception {
    MatriceEntier m = new MatriceEntier(2,3);
    boolean exception = false ;
    try { m.somLigne(-1) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("somLigne(-1) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.somLigne(2) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("somLigne(2) leve une exception", exception);
    bilanAssertions++ ;
  }

  public void test_precondition5() throws Exception {
    MatriceEntier m = new MatriceEntier(2,3);
    boolean exception = false ;
    try { m.somColonne(-1) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("somColonne(-1) leve une exception", exception);
    bilanAssertions++ ;

    exception = false ;
    try { m.somColonne(3) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("somColonne(3) leve une exception", exception);
    bilanAssertions++ ;
  }

  public void test_precondition6() throws Exception {
    MatriceEntier m = new MatriceEntier(2,3);
    boolean exception = false ;
    try { m.setPremiereDiagonale(99) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("setPremiereDiagonale(99) leve une exception", exception);
    bilanAssertions++ ;
  }

  public void test_precondition7() throws Exception {
    MatriceEntier m = new MatriceEntier(2,3);
    boolean exception = false ;
    try { m.setSecondeDiagonale(99) ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("setSecondeDiagonale(99) leve une exception", exception);
    bilanAssertions++ ;
  }

  public void test_precondition8() throws Exception {
    MatriceEntier m = new MatriceEntier(2,3);
    boolean exception = false ;
    try { m.estDiagonale() ; }
    catch (Exception e) { exception = true ; };

    totalAssertions++ ;
    assertTrue("estDiagonale() leve une exception", exception);
    bilanAssertions++ ;
  }

  /*
   main() de la classe de Test
  */
  public static void main(String[] args) {
    junit.textui.TestRunner.run(new TestSuite(MatriceEntierPreconditionsTest.class));
    if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
    else  { System.out.print("OUPS !"); }
    System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
  } // fin main

} // fin MatriceEntierPreconditionsTest
Programme de Test des Axiomes
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;

public class MatriceEntierAxiomesTest extends TestCase {
  static int totalAssertions = 0;
  static int bilanAssertions = 0;

  /*
   Axiomes du type MatriceEntier
  */
  public void test_get() throws Exception {
    MatriceEntier m = new MatriceEntier(3,4) ;

    totalAssertions++ ;
    assertEquals("getNbLignes() == 3", 3, m.getNbLignes());
    bilanAssertions++ ;

    totalAssertions++ ;
    assertEquals("getNbColonnes() == 4", 4, m.getNbColonnes());
    bilanAssertions++ ;

    for (int i=0; i<m.getNbLignes(); i++) {
     for (int j=0; j<m.getNbColonnes(); j++) {
      totalAssertions++ ;
      assertEquals("getElement("+i+","+j+") == 0", 0, m.getElement(i,j));
      bilanAssertions++ ;
     }
    }

    m = new MatriceEntier(3,3) ;
    totalAssertions++ ;
    assertEquals("setPremiereDiagonale(99).getNbLignes() == getNbLignes()", m.setPremiereDiagonale(99).getNbLignes(), m.getNbLignes());
    bilanAssertions++ ;

    totalAssertions++ ;
    assertEquals("setSecondeDiagonale(99).getNbLignes() == getNbLignes()", m.setSecondeDiagonale(99).getNbLignes(), m.getNbLignes());
    bilanAssertions++ ;

    m = new MatriceEntier(3,3) ;
    m.setPremiereDiagonale(99) ;
    for (int i=0; i<m.getNbLignes(); i++) {
     for (int j=0; j<m.getNbColonnes(); j++) {
      totalAssertions++ ;
      if ( i == j ) {
        assertEquals("getElement("+i+","+i+") == 99", 99, m.getElement(i,i));
      } else {
        assertEquals("getElement("+i+","+j+") == 0", 0, m.getElement(i,j));
      }
      bilanAssertions++ ;
     }
    }

  } // fin test_get

  public void test_som() throws Exception {
    MatriceEntier m = new MatriceEntier(3,4) ;

    for (int i=0; i<m.getNbLignes(); i++) {
      totalAssertions++ ;
      assertEquals("somLigne("+i+") == 0", 0, m.somLigne(i));
      bilanAssertions++ ;
    }
    for (int j=0; j<m.getNbColonnes(); j++) {
      totalAssertions++ ;
      assertEquals("somColonne("+j+") == 0", 0, m.somColonne(j));
      bilanAssertions++ ;
    }

    m = new MatriceEntier(3,3) ;
    m.setPremiereDiagonale(99) ;
    for (int ij=0; ij<m.getNbLignes(); ij++) {
      totalAssertions = totalAssertions + 2 ; ;
      assertEquals("setPremiereDiagonale(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
      bilanAssertions++ ;
      assertEquals("setPremiereDiagonale(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
      bilanAssertions++ ;
    }

    m = new MatriceEntier(3,3) ;
    m.setSecondeDiagonale(99) ;
    for (int ij=0; ij<m.getNbLignes(); ij++) {
      totalAssertions = totalAssertions + 2 ; ;
      assertEquals("setSecondeDiagonale(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
      bilanAssertions++ ;
      assertEquals("setSecondeDiagonale(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
      bilanAssertions++ ;
    }

  } // fin test_som

  public void test_est() throws Exception {
    MatriceEntier m = new MatriceEntier(3,3) ;
    totalAssertions++ ;
    assertTrue("estCarree() == true", m.estCarree());
    bilanAssertions++ ;

    m = new MatriceEntier(2,3) ;
    totalAssertions++ ;
    assertFalse("estCarree() == false", m.estCarree());
    bilanAssertions++ ;

    m = new MatriceEntier(3,3) ;
    totalAssertions++ ;
    assertTrue("estDiagonale() == true", m.estDiagonale());
    bilanAssertions++ ;

    m.setPremiereDiagonale(99);
    totalAssertions++ ;
    assertTrue("setPremiereDiagonale(99).estDiagonale() == true", m.estDiagonale());
    bilanAssertions++ ;

    m.setSecondeDiagonale(99);
    totalAssertions++ ;
    assertFalse("setSecondeDiagonale(99).estDiagonale() == false", m.estDiagonale());
    bilanAssertions++ ;

  } // fin test_est

  public void test_mul() throws Exception {
    MatriceEntier m = new MatriceEntier(3,3) ;
    m.setPremiereDiagonale(1).mulMatNombre(99) ;
    for (int ij=0; ij<m.getNbLignes(); ij++) {
      totalAssertions = totalAssertions + 2 ; ;
      assertEquals("setPremiereDiagonale(1).mulMatNombre(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
      bilanAssertions++ ;
      assertEquals("setPremiereDiagonale(1).mulMatNombre(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
      bilanAssertions++ ;
    }

    m = new MatriceEntier(3,3) ;
    m.setSecondeDiagonale(1).mulMatNombre(99) ;
    for (int ij=0; ij<m.getNbLignes(); ij++) {
      totalAssertions = totalAssertions + 2 ; ;
      assertEquals("setSecondeDiagonale(1).mulMatNombre(99).somLigne("+ij+") == 99", 99, m.somLigne(ij));
      bilanAssertions++ ;
      assertEquals("setSecondeDiagonale(1).mulMatNombre(99).somColonne("+ij+") == 99", 99, m.somColonne(ij));
      bilanAssertions++ ;
    }

    MatriceEntier m_init = new MatriceEntier(3,3) ;
    m = new MatriceEntier(3,3) ;
    // Initialise m_init et m à {{0,1,2}{3,4,5}{6,7,8}}
    int k = 0 ;
    for (int i=0; i<m.getNbLignes(); i++) {
     for (int j=0; j<m.getNbColonnes(); j++) {
	m_init.setElement(i,j,k) ;
	m.setElement(i,j,k) ;
	k = k + 1 ;
     }
    }
    m.mulMatNombre(3) ;
    for (int i=0; i<m.getNbLignes(); i++) {
     for (int j=0; j<m.getNbColonnes(); j++) {
      totalAssertions++ ;
      assertEquals("m.mulMatNombre(3).getElement("+i+","+j+") == m.getElement("+i+","+j+") * 3", m_init.getElement(i,j) * 3, m.getElement(i,j));
      bilanAssertions++ ;
     }
    }

  } // fin test_mul

  /*
   main() de la classe de Test
  */
  public static void main(String[] args) {
    junit.textui.TestRunner.run(new TestSuite(MatriceEntierAxiomesTest.class));
    if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
    else  { System.out.print("OUPS !"); }
    System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
  } // fin main

} // fin MatriceEntierAxiomesTest
Programme de Test des Opérations supplémentaires
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;

public class MatriceEntierOpSupTest extends TestCase {
  static int totalAssertions = 0;
  static int bilanAssertions = 0;

  /*
   Opérations supplémentaires du type MatriceEntier
  */
  public void test_toString() throws Exception {
    MatriceEntier m = new MatriceEntier(3,3) ;
    m.setPremiereDiagonale(1).setSecondeDiagonale(2) ;

    String ln = System.getProperty("line.separator") ;
    String attendu = "1 0 2 " + ln + "0 2 0 " + ln + "2 0 1 " + ln ;
    totalAssertions++ ;
    assertEquals("toString() == ", attendu, m.toString());
    bilanAssertions++ ;
  }

  public void test_toHTML() throws Exception {
    MatriceEntier m = new MatriceEntier(3,3) ;
    m.setPremiereDiagonale(1).setSecondeDiagonale(2) ;

    String ln = System.getProperty("line.separator") ;
    String attendu = "<table border=\"1\">" + ln ;
    attendu += "<tr><td>1</td><td>0</td><td>2</td></tr>" + ln + "<tr><td>0</td><td>2</td><td>0</td></tr>" + ln + "<tr><td>2</td><td>0</td><td>1</td></tr>" + ln ;
    attendu += "</table>" + ln ;
    totalAssertions++ ;
    assertEquals("toHTML() == ", attendu, m.toHTML());
    bilanAssertions++ ;
  }

  /*
   main() de la classe de Test
  */
  public static void main(String[] args) {
    junit.textui.TestRunner.run(new TestSuite(MatriceEntierOpSupTest.class));
    if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
    else  { System.out.print("OUPS !"); }
    System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions verifiees");
  } // fin main

} // fin PileTest

Le programme de test fourni est :

Tests d’intégration

titanic

Test d’intégration

Plus délicat, il s’agit :

 

Programme de Test du programme JourSuivantAvecLibDate.class
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;
import java.io.*;

public class JourSuivantAvecLibDateTest extends TestCase {
  static String programmeATester = "JourSuivantAvecLibDate" ;  (1)
  Process executionProgrammeATester ;                          (2)
  BufferedReader ecranProgrammeATester ;                       (3)
  BufferedWriter clavierProgrammeATester ;                     (4)

  String finDeLigne = System.getProperty("line.separator") ;   (5)

  public static void main(String[] args) {
    if ( args.length > 0 ) { programmeATester = args[0] ; }
    System.out.println("Tests du programme : " + programmeATester);
    junit.textui.TestRunner.run(new TestSuite(JourSuivantAvecLibDateTest.class)); (6)
  }

  protected void setUp() throws IOException {  (7)
    executionProgrammeATester = Runtime.getRuntime().exec("java -cp . "+programmeATester); (8)
    ecranProgrammeATester = new BufferedReader(new  InputStreamReader( executionProgrammeATester.getInputStream() )); (9)
    clavierProgrammeATester  = new BufferedWriter(new OutputStreamWriter( executionProgrammeATester.getOutputStream() )); (10)
  }

  // Saisies valides
  public void test_31_1_2013() throws IOException {
    assertEquals("Affiche : 'Saisir une date : jour mois annee ? '","Saisir une date : jour mois annee ? ",ecranProgrammeATester.readLine()); (11)
    clavierProgrammeATester.write("31 1 2013"+finDeLigne); (12)
    clavierProgrammeATester.flush();                       (13)
    assertEquals("Affiche : 'Le lendemain du 31/1/2013'","Le lendemain du 31/1/2013",ecranProgrammeATester.readLine());
    assertEquals("Affiche : 'sera le 1/2/2013.'","sera le 1/2/2013.",ecranProgrammeATester.readLine()); (14)
  }

  public void test_28_2_2013() throws IOException {
    String messageSaisie = "Saisir une date : jour mois annee ? " ;
    String[] ligneJeuDEssai = {"28 2 2013","Le lendemain du 28/2/2013","sera le 1/3/2013."} ;

    assertEquals("Affiche : "+messageSaisie,messageSaisie,ecranProgrammeATester.readLine());
    clavierProgrammeATester.write(ligneJeuDEssai[0]+finDeLigne); clavierProgrammeATester.flush();
    assertEquals("Affiche : "+ligneJeuDEssai[1],ligneJeuDEssai[1],ecranProgrammeATester.readLine());
    assertEquals("Affiche : "+ligneJeuDEssai[2],ligneJeuDEssai[2],ecranProgrammeATester.readLine());
  }

  protected void assertsPourSaisieValide(String messageSaisie,String saisie,String affichage1,String affichage2) throws IOException {
     assertEquals("Affiche : "+messageSaisie,messageSaisie,ecranProgrammeATester.readLine());
     clavierProgrammeATester.write(saisie+finDeLigne); clavierProgrammeATester.flush();
     assertEquals("Affiche : "+affichage1,affichage1,ecranProgrammeATester.readLine());
     assertEquals("Affiche : "+affichage2,affichage2,ecranProgrammeATester.readLine());
  }
  public void test_31_3_2013() throws IOException {
    String messageSaisie = "Saisir une date : jour mois annee ? " ;
    String[] ligneJeuDEssai = {"31 3 2013","Le lendemain du 31/3/2013","sera le 1/4/2013."} ;
    assertsPourSaisieValide(messageSaisie,ligneJeuDEssai[0],ligneJeuDEssai[1],ligneJeuDEssai[2]);
  }

  // Saisies invalides
  protected void assertsPourSaisieInvalide(String messageSaisie,String saisie,String affichage) throws IOException {
     assertEquals("Affiche : "+messageSaisie,messageSaisie,ecranProgrammeATester.readLine());
     clavierProgrammeATester.write(saisie+finDeLigne); clavierProgrammeATester.flush();
     assertEquals("Affiche : "+affichage,affichage,ecranProgrammeATester.readLine());
  }

  public void test_1_1_1581() throws IOException {
    String messageSaisie = "Saisir une date : jour mois annee ? " ;
    String[] ligneJeuDEssai = {"1 1 1581","La date du 1/1/1581 n'est pas une date valide."} ;
    assertsPourSaisieInvalide(messageSaisie,ligneJeuDEssai[0],ligneJeuDEssai[1]);
  }

  public void test_32_1_2013() throws IOException {
    String messageSaisie = "Saisir une date : jour mois annee ? " ;
    String[] ligneJeuDEssai = {"32 1 2013","La date du 32/1/2013 n'est pas une date valide."} ;
    assertsPourSaisieInvalide(messageSaisie,ligneJeuDEssai[0],ligneJeuDEssai[1]);
  }

} // fin class
1nom de l’application (du programme) à tester
2Process (Processus) = programme en cours d’exécution
3lien vers l’écran du programme en cours d’exécution
4lien vers le clavier du programme en cours d’exécution
5récupération portable du retour à la ligne
6lancement de toutes les fonctions débutant par 'test'
7fonction (ré-)exécutée avant chaque fonction de test et qui exécute le programme à tester
8lance le programme
9se connecte à l’écran (sortie standard) du programme lancé
10se connecte au clavier (entrée standard) du programme lancé
11lit une ligne sur l’écran du programme lancé
12écrit une ligne au clavier du programme lancé
13force l’envoi de la ligne au clavier (vide le tampon de sortie)
14lit une autre ligne sur l’écran du programme lancé

Les tests implémentent des algorithmes simples

public void test_dates_invalides() {
  int[][] tabJeuDEssaiDatesInvalides = {  (1)
      {1,1,1581},{0,1,2013},{99,99,2099},
      {32,1,2013},{29,2,2013},{32,3,2013},
      {31,4,2013},{32,5,2013},{31,6,2013},
      {32,7,2013},{32,8,2013},{31,9,2013},
      {32,10,2013},{31,11,2013},{32,12,2013},
      {29,2,1900},{30,2,2000}
  } ;
  for ( int indice = 0, taille = tabJeuDEssaiDatesInvalides.length;
        indice < taille ;
        indice = indice + 1){
    int[] date = tabJeuDEssaiDatesInvalides[indice] ;
    assertFalse(date[0]+"/"+date[1]+"/"+date[2]+" est invalide"
    , LibDate.dateValide(date[0],date[1],date[2])); (2) (3)
  }
  bilanAssertions = bilanAssertions + tabJeuDEssaiDatesInvalides.length ;
}
1given : dans les situations suivantes
2when : quand on vérifie la validité de la date
3then : on doit obtenir false

Tout est-il testable ?

Toutes les manières de faire sont exploitables

  • Ecrire le programme qui fasse passer les tests fournis
  • Ecrire les tests d’un programme fourni
  • Coder une fonctionnalité et ajouter le test correspondant
  • Ajouter un test et intégrer la fonctionnalité correspondante (TDD)
  • Retrouver l’équilibre du couple Programme / Programme de Test

Les erreurs les plus courantes

http://blog.takipi.com/the-top-10-exceptions-types-in-production-java-applications-based-on-1b-events/?utm_content=buffer0c58b&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer

Au menu du module M3301/MPA

Réaliser le développement d’une application logicielle en utilisant la méthode SCRUM et qui se termine dans de bonnes conditions.

Chaque groupe dispose de ses force vives et de 4 TD/TP par semaine pour réaliser le sprint courant (fournir tous les livrables).

Ready for a quizz?

tuxteacher

 

QUESTION
  • Connectez-vous sur : http://www.socrative.com/ (student login)
  • Ou téléchargez l’application pour étudiant socrative2
  • Choisissez la room 44918d67
socrative1

The End    (for now)